﻿// CDecisionTree.cs
// Author: Gant Briseno
#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Casters.source.Objects;
using Casters.source.States;
using Casters.source.Managers;
using Casters.source.Objects.Spells;
#endregion

namespace Casters.source.AI
{
    /// <summary>
    /// A tree that can be traversed and
    /// is used for AI decision making
    /// </summary>
    public class CDecisionTree
    {
        /// <summary>
        /// Function for executing a decision by the AI
        /// </summary>
        /// <returns></returns>
        public delegate bool AICallback(CPlayer player, CComputerPlayer computer);

        public class CDecisionNode
        {
            // Variables
            public AICallback m_Function;
            public List<CDecisionNode> m_Nodes;
            public float m_Position; // Used for ordering the tree

            public CDecisionNode()
            {
                m_Function = null;
                m_Nodes = null;
            }

            public CDecisionNode(AICallback func)
            {
                m_Function = func;
                m_Nodes = new List<CDecisionNode>();
            }

            public void Load(BinaryReader br, int version)
            {
                if (version == 0)
                {
                    string function = br.ReadString();
                    switch (function)
                    {
                        default:
                            m_Function = null;
                            break;
                    }

                    m_Nodes = new List<CDecisionNode>();
                }
            }
        }

        // Variables
        protected CDecisionNode m_Root;
        protected CDecisionNode m_CurNode;

        public CDecisionTree()
        {
            m_Root = null;
            m_CurNode = null;
        }

        public void LoadContent()
        {
        }

        public void UnloadContent()
        {
            UnloadTree(m_Root);
            m_Root = null;
            m_CurNode = null;
        }

        public void RunAI(CDecisionNode _node)
        {
            foreach (CDecisionNode node in _node.m_Nodes)
            {
                if (node.m_Function(CClassState.GetInstance().GetPlayer(), CClassState.GetInstance().GetComputer()))
                    RunAI(node);
            }
        }

        public void UnloadTree(CDecisionNode node)
        {
            if (node.m_Nodes.Count > 0)
                foreach (CDecisionNode n in node.m_Nodes)
                    UnloadTree(n);

            node = null;
        }

        public void LoadTree()
        {
            int version = 0;
            int count = 0;
            CDecisionNode temp = null;

            string filepath = "temp";
            using (BinaryReader br = new BinaryReader(TitleContainer.OpenStream(filepath)))
            {
                version = br.ReadInt32();

                if (version == 0)
                {
                    temp = new CDecisionNode();
                    temp.Load(br, version);
                    count = br.ReadInt32();
                    LoadChildren(br, temp, count, version);
                }
            }
        }

        public void LoadChildren(BinaryReader br, CDecisionNode node, int count, int version)
        {
            CDecisionNode temp = null;
            int childCount = 0;

            if (version == 0)
            {
                for (int i = 0; i < count; ++i)
                {
                    temp = new CDecisionNode();
                    temp.Load(br, version);

                    // Children Count
                    childCount = br.ReadInt32();

                    if (childCount > 0)
                        LoadChildren(br, temp, childCount, version);

                    node.m_Nodes.Add(temp);
                }
            }
        }

        /// <summary>
        /// Check the computer's distance from the player
        /// </summary>
        /// <param name="player"></param>
        /// <param name="computer"></param>
        /// <returns>True if the player is not within range of a spell</returns>
        public bool CheckDistance(CPlayer player, CComputerPlayer computer)
        {
            float distance = 0;
            distance = Vector2.Distance(player.m_vPositionOnMap, computer.m_vPositionOnMap);

            // Quick check of spell ranges
            // Easy difficulty
            for (int i = 0; i < 6; ++i)
            {
                if (distance < CSpellManager.GetInstance().GetSpell(computer.GetSpell(i)).GetRange())
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Checks which direction to move and moves
        /// 
        /// NOTE: Fog of War not in yet
        /// </summary>
        /// <param name="player"></param>
        /// <param name="computer"></param>
        /// <returns>Returns true if you can't see the player</returns>
        public bool CheckDirectionMove(CPlayer player, CComputerPlayer computer)
        {
            // If you have no ticks left kick out
            if (computer.GetTickMax())
                return false;

            Vector2 direction = Vector2.Zero;
            Vector2 temp = computer.m_vPositionOnMap - player.m_vPositionOnMap;

            if (Math.Abs(temp.X) > Math.Abs(temp.Y))
            {
                if (temp.X > 0)
                    direction.X = 1;
                else
                    direction.X = -1;
            }
            else
            {
                if (temp.Y > 0)
                    direction.Y = 1;
                else
                    direction.Y = -1;
            }

            computer.m_vMoveDirection = direction;
            CEventSystem.BroadcastEvent(EventID.AddAction, computer, 1, 1, EventID.Setup_AddMove, computer.m_vMoveDirection, null);
            computer.IncrementTick(4);

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="player"></param>
        /// <param name="computer"></param>
        /// <returns></returns>
        public bool CheckAttack(CPlayer player, CComputerPlayer computer)
        {
            // If you have no ticks left kick out
            if (computer.GetTickMax())
                return false;

            float distance = 0;
            distance = Vector2.Distance(player.m_vPositionOnMap, computer.m_vPositionOnMap);

            // Quick check of spell ranges
            // Easy difficulty
            for (int i = 0; i < 6; ++i)
            {
                CSpell tempSpell = CSpellManager.GetInstance().GetSpell(computer.GetSpell(i));
                if (distance < tempSpell.GetRange() && tempSpell.GetPower() > 0)
                {
                    CEventSystem.BroadcastEvent(EventID.AddAction, computer, computer.GetTick(), 1, EventID.Execute_CastSpell, computer.m_vCastPosition, computer.GetSpell(i));
                    computer.IncrementTick(tempSpell.GetCooldown());
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="player"></param>
        /// <param name="computer"></param>
        /// <returns></returns>
        public bool FinalCheckAttack(CPlayer player, CComputerPlayer computer)
        {
            // If you have no ticks left kick out
            if (computer.GetTickMax())
                return false;

            return true;
        }
    }
}
